#include <termio.h>
#include <iostream>
#include <string_view>
#include <libime/table/tablebaseddictionary.h>
#include <libime/core/userlanguagemodel.h>
#include <libime/table/tablecontext.h>
#include <libime/table/tableoptions.h>
#include <libime/table/tableoptions.h>


#define COLOR_RESET   "\033[0m"
#define COLOR_RED     "\033[31m"  

using namespace libime;

auto select_id = -1;
auto page_start = 0;
TableContext::CandidateRange candidates;


enum 
{
    MODE_INPUT,
    MODE_ESC,
    MODE_ARROW,
}e_mode = MODE_INPUT;

void show_candicates(TableContext &ctx) 
{
    auto page_end = page_start+5<candidates.size() ?page_start+5: candidates.size();
    
    std::cout << "\n\nCandidates: ";
    for(auto i=page_start; i<page_end; i++) {
        std::cout << i%5+1 <<"、";
        if(i==select_id) std::cout << COLOR_RED;
        std::cout << candidates[i].toString();
        if(i==select_id) std::cout << COLOR_RESET;
        std::cout << " ";
    }
    std::cout << "\nType/Select(p" << page_start/5+1 << '/' <<  (candidates.size()+4)/5 << "):" << ctx.userInput();
}

int main(int argc, char *argv[])
{
    TableBasedDictionary dict;
    dict.load("data/yipy.main.dict.txt", TableFormat::Text);

    TableOptions opts;
    opts.setOrderPolicy(OrderPolicy::Freq);
    opts.setNoSortInputLength(2);
    opts.setAutoSelect(true);
    opts.setAutoSelectLength(-1);
    opts.setNoMatchAutoSelectLength(-1);
    opts.setCommitRawInput(0);
    opts.setMatchingKey(0x2a); //*
    opts.setEndKey(std::set<uint32_t>());
    opts.setExactMatch(false);
    opts.setLearning(true);
    opts.setAutoPhraseLength(4);
    opts.setSaveAutoPhraseAfter(3);
    opts.setAutoRuleSet(std::unordered_set<std::string>{"e2"});
    opts.setLanguageCode("yi_CN");
    dict.setTableOptions(opts);
    
    UserLanguageModel model("data/kenlm_yi.arpa");
    TableContext ctx(dict, model);


    struct termios new_settings;
    struct termios stored_settings;
    tcgetattr(0 ,&stored_settings);
    new_settings = stored_settings;
    new_settings.c_lflag &= ~(ICANON|ECHO);
    new_settings.c_cc[VTIME] = 0;
    new_settings.c_cc[VMIN] = 1;
    tcsetattr(0,TCSANOW,&new_settings);
 
    std::cout <<  "\n"
        "\tType a-z to input.\n"
        "\tPress up/down key to switch candidates page, press left/right arrow key to switch the selected candidate.\n"
        "\tType 1-5 to commit a candidate, press space key will commit the current selected candidate.\n"
        "\tPress enter key will commit the user input.\n"
        "Type(a-z):";
    int ch;
    while((ch=getchar())!=EOF)
    {
        switch(e_mode) 
        {
        case MODE_INPUT:
            switch(ch)
            {
            case 27: //ESC
                e_mode = MODE_ESC;
                continue;
            case 127: //BackSpace
                ctx.backspace();
                goto CANDIDATES_UPDATED;
                break;
            case 32: //Space
                goto CANDIDATES_SELECTED;
            case 10: //Enter
                std::cout << "\nCurrent input is selected:"  << ctx.userInput() << '\n';             
                goto INPUT_SELECTED;
            default:
                if(ch>96 && ch<123 || ch=='*')  //a-z, *
                {
                    std::cout << (char)ch;
                    ctx.type(ch);
                    goto CANDIDATES_UPDATED;
                } else if(ch>48 && ch<54) //1-5
                {
                    if(page_start+ch-49<candidates.size())
                    {
                        std::cout << (char)ch;
                        select_id = page_start+ch-49;
                        goto CANDIDATES_SELECTED;
                    }
                        
                }    
                else 
                    goto INPUT_ERROR;
            }
            break;
        case MODE_ESC:
            if(ch == 91)  
            {
                e_mode = MODE_ARROW;
                continue;
            }
            else if(ch == 27) goto INPUT_END; //double Esc: exit
            else goto INPUT_ERROR;
        case MODE_ARROW:
            e_mode = MODE_INPUT;
            switch(ch)
            {
            case 65:  //up
                if(page_start>0) 
                {
                    page_start -= 5;
                    select_id = page_start;
                    goto SHOW_CANDIDATES;
                }
            case 66: //down
                if(page_start+5 < candidates.size())
                {
                    page_start += 5;
                    select_id = page_start;
                }
                goto SHOW_CANDIDATES;
            case 67: //right
                if(select_id+1 < candidates.size()) 
                {
                    select_id++;
                    if(select_id%5 == 0) page_start = select_id;
                }
                goto SHOW_CANDIDATES;
            case 68: //left
                if(select_id > 0) {
                    select_id--;
                    if(select_id%5 == 4) page_start-=5;
                }
                goto SHOW_CANDIDATES;
            default: goto INPUT_ERROR;
            }
        default:
            break;
        }
INPUT_ERROR:
        std::cout << "\nInvalid input:`" << char(ch) << "`!!\n";
        goto SHOW_CANDIDATES;
CANDIDATES_SELECTED:
        if(candidates.size()>select_id)
        {
            std::cout << "\nSelected candidate:" <<  candidates[select_id].toString();
            ctx.select(select_id);   
            ctx.learn();
        }
INPUT_SELECTED:
        e_mode = MODE_INPUT;
        ctx.clear();
CANDIDATES_UPDATED:
        candidates = ctx.candidates();
        select_id = 0;
        page_start = 0;
SHOW_CANDIDATES:
        show_candicates(ctx);
    } 

INPUT_END: 
    tcsetattr(0,TCSANOW,&stored_settings);
    return 0;
}
